//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <string.h>
#include <elatypes.h>

void* __cdecl memmove(void* pDest, const void* pSource, size_t count)
{
#ifdef _GNUC

#if defined(_x86)
    int temp0, temp1, temp2;
    if (pDest < pSource) {
        ASM("rep;"
            "movsd;"//movs DoubleWord(4 btyes) one time
            "testb $2,%b6;"//0x03H--11b, 0x02H--10b, 0x01H--01b
            "je 1f;"
            "movsw;"
            "1:"
            "testb $1,%b6;"
            "je 2f;"
            "movsb;"
            "2:"
            :"=D" (temp0), "=S" (temp1), "=c" (temp2)
            :"0" ((long)pDest),"1" ((long)pSource), "2" (count / 4), "a" (count)
            : "memory");
    }
    else {
        ASM("std;"//set DF=1
            "cmp $4, %6;"
            "js 1f;"
            "sub $4, %0;"
            "sub $4, %1;"
            "rep;"
            "movsd;"
            "add $4, %0;"
            "add $4, %1;"
            "1:"
            "sub $1, %0;"
            "sub $1, %1;"
            "movl %7, %2;"
            "rep;"
            "movsb;"
            "cld;"//reset DF=0
            :"=D" (temp0), "=S" (temp1), "=c" (temp2)
            :"0" ((long)pDest + count),
             "1" ((long)pSource + count),
             "2" (count / 4),
             "a" (count),
             "d" (count % 4)
            :"memory");
    }
    return pDest;
#endif    //if defined(_x86)

#endif    //ifdef _GNUC

    _ELASTOS Boolean naturalAlignment;
    int delta;
    unsigned long i;
    void* pRet = pDest;

    if (((Address)pSource | (Address)pDest | count) & (sizeof(void*)-1)) {
        delta = 1;                      // copy one byte at a time
        naturalAlignment = FALSE;
    }
    else {
        delta = sizeof(int);            // natural alignment size
        count /= sizeof(int);           // copy this many machine words
        naturalAlignment = TRUE;
    }

    if (pDest > pSource) {              // copy in descending order
        int offset = count * delta - delta;
        pSource = (void*)((byte*)pSource + offset);
        pDest = (void*)((byte*)pDest + offset);
        delta = -delta;
    }

    if (naturalAlignment) {
        for (i = 0; i < count; i++) {   // copy one machine word at a time
            *((int*) pDest) = *((int*) pSource);
            pDest = (void*)((byte*)pDest + delta);
            pSource = (void*)((byte*)pSource + delta);
        }
    }
    else {
        for (i = 0; i < count; i++) {   // copy one byte at a time
            *((byte*) pDest) = *((byte*) pSource);
            pDest = (void*)((byte*)pDest + delta);
            pSource = (void*)((byte*)pSource + delta);
        }
    }

    return pRet;
}
